/* 
 * File:   Game.cpp
 * Author: mvdr
 * 
 * Created on March 19, 2013, 3:01 PM
 */

#include "Game.h"
#include <algorithm>
#include <iostream>
#include <utility>
#include <queue>
#include "Utilities.h"
#include <map>

using namespace std;

Game::Game(bool magic, bool verbose):
d_questionStack(0),
d_stateStack(0),
d_currState(0),
d_stateCounter(0),
d_currPlayer(0),
d_magic(magic),
d_verbose(verbose)        
{    
    for (size_t i = 1; i != d_questions.size(); ++i)
        d_questionStack.push_back(i);
    
    for (size_t i = 1; i != d_model.numberOfStates(); ++i)
        d_stateStack.push_back(i);
    
    random_shuffle(d_stateStack.begin(), d_stateStack.end());
}

vector<size_t> Game::run() {
    shuffleQuestions();
    d_model.initRelations();    
    d_currState = d_stateStack[d_stateCounter++];    
    
    return startPlaying();
}

void Game::shuffleQuestions() {
    random_shuffle(d_questionStack.begin(), d_questionStack.end());
}

vector<size_t> Game::startPlaying() {
    for (size_t turn = 0; turn != 3 * d_questionStack.size(); ++turn)    
    {   
        
        if (d_verbose)
        {
            cout << "\033[2J\033[1;1H" 
                << "=======================================================\n"
                << "GAME " << prettyString(d_stateCounter, 4) << " // "
                << "TURN " << prettyString(turn + 1, 2) << " // TRUE STATE: "
                << d_model.stateAt(d_currState) << '\n'
                << "=======================================================\n\n";
            printInfo();
        }        
        
        d_currPlayer = turn % 3;
        int currQuestion = d_questionStack[turn % d_questionStack.size()];        
        
        State trueState = d_model.stateAt(d_currState);
        
        bool answer = d_questions.answer(d_currPlayer, currQuestion, trueState); 
        
        if (d_verbose)
            d_questions.printAnswer(d_currPlayer, currQuestion, answer);
            
        
        updateModel(answer, currQuestion);
        
        if(anybodyKnows())
        {
            if (d_verbose)
            {
                cout << '\r' << numberOfWinners() << " winner";
                if (numberOfWinners() > 1)
                    cout << 's';
                cout << " after " << turn + 1 << " turns!!!\n";
            
                cout << "(continue)";
                pauseTilEnter();
            }
            return vector<size_t> ({numberOfWinners(), turn + 1});
        }
        else if (turn < 3 * d_questionStack.size() - 1 && d_magic)
        {
            if (d_verbose)
            {
                cout << "\r(continue)  \b\b";
                pauseTilEnter();         
            }           
            epistemagic();
        }
        
        bool considerTrueStatePossible = 
        d_model.playerConsidersStatePossible(0, d_currState, d_currState) &
        d_model.playerConsidersStatePossible(1, d_currState, d_currState) &
        d_model.playerConsidersStatePossible(2, d_currState, d_currState);
        
        if (!considerTrueStatePossible)
            cout << "\nERROR!!! True state not considered possible by at " <<
                    "least one player!\n";
        
        if (d_verbose)
        {
            cout << "(continue)  \b\b";
            pauseTilEnter();         
        }
    }
    cout << "No winners :-(\n";
    return vector<size_t> ({0,0});
    
}

void Game::updateModel(bool answer, int currQuestion) {
    
    vector<vector<int> > printStates;
    for (int p = 0; p != 3; ++p)    
        printStates.push_back(vector<int> (0));
    
    int dots = 0;    
    
    for (size_t p = 0; p != 3; ++p)
    {
        for (size_t idx = 0; idx != d_model.numberOfStates(); ++idx) {         
            if (d_magic & d_verbose & (idx % 5 == 0)) {
                cout << "\rworking";
                switch(++dots % 4) {
                    case 1:
                        cout << ".  ";
                        break;
                    case 2:
                        cout << ".. ";
                        break;
                    case 3:
                        cout << "...";
                        break;
                    default:
                        cout << "   ";
                }
            }
            if(d_magic || d_model.playerConsidersStatePossible(p, d_currState, idx))
            {
                State tempState = d_model.stateAt(idx);
                bool tempAnswer = d_questions.answer(d_currPlayer, currQuestion, tempState);
                if (tempAnswer != answer)
                {
                    if (d_model.playerConsidersStatePossible(p, d_currState, idx))
                            printStates[p].push_back(idx);
                    d_model.cutOff(idx, p);                    
                }
            }
        }
    }
    
    cout << '\r';
    
    if (d_verbose)
        for (size_t p = 0; p != 3; ++p)
        {
            if ( p != d_currPlayer)
            {
                if (printStates[p].empty())
                    cout << "Nothing has changed for player " << p + 1 << ".\n\n";
                else
                {
                    cout << "Player " << p + 1 << " no longer considers the"
                            << " following states possible:";
                    size_t counter = 0;
                    for (size_t i = 0; i != printStates[p].size(); ++i)
                    {        
                        if (counter++ % 3 == 0)
                                cout << '\n';
                        cout << d_model.stateAt(printStates[p][i]) << "; ";                    
                    }
                    cout << "\b\b.\n\n";


                }
            }
        }
        
    
}

bool Game::anybodyKnows() const {    
    for (size_t p = 0; p != 3; ++p)
    {
        if (d_model.playerKnowsState(p, d_currState))
            return true;
    }
    return false;
}

size_t Game::numberOfWinners() const {
    size_t winners = 0;
    for (size_t p = 0; p != 3; ++p)
    {
        if (d_model.playerKnowsState(p, d_currState))
            ++winners;
    }
    
    return winners;
}

void Game::epistemagic() {
    vector<int> removeCandidates(0);
    map<int, vector<int> > printStates;
    
    for (int p = 0; p != 3; ++p)        
        for (size_t s = 0; s != d_model.numberOfStates(); ++s)        
            if (d_model.playerKnowsState(p, s))                         
            {
                removeCandidates.push_back(s);                
                if (printStates.count(s) == 0)
                    printStates[s] = vector<int>(1, p);
                else
                    printStates[s].push_back(p);
            }
    
    vector<map<int, vector<int> > > playerPrintStates;
    for (size_t p = 0; p != 3; ++p)
    {
        map<int, vector<int> > tmpMap;
        playerPrintStates.push_back(tmpMap);
        for (auto itr = printStates.begin(); itr != printStates.end(); ++itr)
            if (d_model.playerConsidersStatePossible(p, d_currState, itr->first))
                playerPrintStates[p].insert(*itr);
    }
     
    for (size_t i = 0; i != removeCandidates.size(); ++i)
        for (size_t p = 0; p != 3; ++p)
            d_model.cutOff(removeCandidates[i], p);    

            
    
    if (d_verbose)
    {
        cout << "\nAnnouncement: No player knows which cards are on her head!\n\n";
        for (int p = 0; p != 3; ++p)
        {
            if (playerPrintStates[p].empty())
                cout << "Nothing has changed for player " << p + 1 << ".\n\n";
            else
            {
                cout << "Player " << p + 1 << " no longer considers the following"
                        << " states possible:\n";
                for (auto itr = playerPrintStates[p].begin(); 
                        itr != playerPrintStates[p].end(); ++itr)
                {
                    cout << "- " << d_model.stateAt(itr->first) << "\n  ";
                    because(itr->second);                    
                }
                cout << '\n';
            }
        }
    }
    
}

void Game::printInfo() const {
    for (int p = 0; p != 3; ++p)
        d_model.printPossibleStates(p, d_currState);
}

void Game::because(vector<int> const &player)
{
    if (player.size() == 1)
        cout << "(because player " << player[0] + 1 << " would have known her"
             << " cards by now if that\n   was the true state.)\n";
    else
        cout << "(because players " << player[0] + 1 << " and " << player[1] + 1
             << " would have known their cards by now if\n   that was the true"
             << " state.)\n";
}
